package com.navinfo.tripanalysis.common.arithmetic;

import com.navinfo.tripanalysis.common.arithmetic.common.*;
import com.navinfo.tripanalysis.common.arithmetic.convert.EventDataConvert;
import com.navinfo.tripanalysis.common.arithmetic.custom.VarDrivingFatigue;
import com.navinfo.tripanalysis.common.arithmetic.enums.EventStatus;
import com.navinfo.tripanalysis.common.arithmetic.manager.AbstractArithmetic;
import com.navinfo.tripanalysis.common.config.ConfigMap;
import com.navinfo.tripanalysis.common.protocol.java.EventDataPb;
import com.navinfo.tripanalysis.common.util.ArithmeticUtils;

import java.util.Optional;

/**
 * 疲劳驾驶算法<br/>
 * 输出：统计、事件<br/>
 */
public class DrivingFatigue extends AbstractArithmetic<VarDrivingFatigue> {
    /**
     * 事件类型
     */
    private static final EventDataPb.EventType EVENT_FATIGUE_DRIVING = EventDataPb.EventType.fatigueDriving;
    /**
     * 休息时间阀值 单位：分钟转换为ms
     */
    public int restTimeThresholdMs = ArithmeticUtils.timeMinuteToMs(ConfigMap.getInteger("Fatigue_Rest_Minute_Threshold"));;
    /**
     * 休息时长最小阀值，小于这个时间的休息时长，将不统计 单位：分钟转换为ms
     */
    public int minValidRestDurationMs = ArithmeticUtils.timeMinuteToMs(ConfigMap.getInteger("Fatigue_Rest_Event_Minute_Threshold"));
    /**
     * 疲劳驾驶中开车时间的最小阀值 单位：分钟转换为ms
     */
    public int drivingTimeThresholdMs = ArithmeticUtils.timeMinuteToMs(ConfigMap.getInteger("Fatigue_Driving_Minute_Threshold"));

    /**
     * 是否处于疲劳驾驶状态中
     */
    public boolean isFatigueDrivingState() {
        return drivingData.getEventData(EVENT_FATIGUE_DRIVING) != null;
    }

    /**
     * 是否为开车状态<br/>
     * 开车状态判定：发动机转速>500并且车速>0
     * @return
     */
    public boolean isDrivingState() {
        CommonData data = getCommonData();
        return data.getRotation() > 500 && data.getSpeedKmh() > 0;
    }

    /**
     * 是否为休息状态<br/>
     * 休息状态判定：车速=0
     * @return
     */
    public boolean isRestState() {
        return  getCommonData().getSpeedKmh() == 0;
    }

    @Override
    public void calculate() {
        logger.debug("进入疲劳驾驶算法.[terminalId:{}]", terminalId);
        VarDrivingFatigue var = getObj();
        CommonData commonData = getCommonData();
        int dffTime = commonData.getDffTime();

        // 如果为开车状态，累计驾驶时长，打开休息计时开关
        if (isDrivingState()) {
            logger.debug("疲劳驾驶算法：开车状态");
            //打开休息计时开关
            var.setRestTimingSwitch(true);
            //累加驾驶时长
            var.setDrivingTotalTimeMs(var.getDrivingTotalTimeMs() + dffTime);

            //开车时间>=4小时，设置疲劳驾驶事件
            if (var.getDrivingTotalTimeMs() >= drivingTimeThresholdMs) {
                logger.debug("疲劳驾驶算法：开车时间>阀值");
                if (!isFatigueDrivingState()) {
                    logger.debug("疲劳驾驶算法：疲劳驾驶状态开始");
                    //疲劳驾驶次数+1
                    var.setFrequency(var.getFrequency() + 1);
                    //初始化疲劳驾驶事件
                    startEventData(EVENT_FATIGUE_DRIVING);
                }
            }
        }
        //休息计时开关开启
        else if (var.isRestTimingSwitch()) {
            boolean previous = var.isPreRest();
            boolean current= isRestState();
            var.setPreRest(current);

            //计算休息时长
            EventStatus eventStatus = getEventStatus(previous, current);
            switch (eventStatus) {
                case START:
                    logger.debug("疲劳驾驶算法，休息事件开始.[terminalId:{}]", terminalId);
                    break;
                case IN:
                    logger.debug("疲劳驾驶算法，休息事件中.[terminalId:{}]", terminalId);
                    var.setRestEventTimeMs(var.getRestEventTimeMs() + dffTime);
                    break;
                case END:
                    logger.debug("疲劳驾驶算法，休息事件结束.[terminalId:{}]", terminalId);
                    int eventTotalTime = var.getRestEventTimeMs()+dffTime;
                    if(eventTotalTime >= minValidRestDurationMs){
                        var.setRestTotalTimeMs(var.getRestTotalTimeMs() + eventTotalTime);
                        logger.debug("疲劳驾驶算法，休息事件时长大于等于阀值，累计休息时长[terminalId:{}]", terminalId);
                    }else{
                        logger.debug("疲劳驾驶算法，休息事件时长({}}小于阀值({})，不累计休息时长[terminalId:{}]",eventTotalTime, minValidRestDurationMs, terminalId);
                    }

                    //清空休息时长
                    var.setRestEventTimeMs(0);
                    break;
                default:
                    logger.debug("疲劳驾驶算法，休息状态：上一次和本次都未休息，跳出算法.[terminalId:{}]", terminalId);
            }

            // 累积休息时间>=20分钟
            int total = var.getRestTotalTimeMs();
            if(var.getRestEventTimeMs() >= minValidRestDurationMs){
                total += var.getRestEventTimeMs();
            }

            if (total >= restTimeThresholdMs) {
                logger.debug("疲劳驾驶算法：休息时间>阀值，清空相关状态");
                // 检查是否为疲劳状态，并结束疲劳状态
                endFatigueDriving();

                // 清空开车累计时间，休息累计时间，关闭休息计时开关
                var.setDrivingTotalTimeMs(0);
                var.setRestTotalTimeMs(0);
                var.setRestTimingSwitch(false);
                var.setPreRest(false);
                var.setRestEventTimeMs(0);
                var.setRestTimingSwitch(false);
            }
        }

        //行程最后一个点
        if(commonData.isTripLastNode()){
            logger.debug("疲劳驾驶算法collect,结束未结束的疲劳驾驶事件.[terminalId:{}]", terminalId);
            endFatigueDriving();
        }

        logger.debug("疲劳驾驶算法结束.[terminalId:{}]", terminalId);
    }

    @Override
    public void collect(OuterStatisticData statisticDataOut) {
        VarDrivingFatigue var = getObj();
        statisticDataOut.setFatigueDrivingNumber(var.getFrequency());
        //疲劳驾驶时长=行程总时长-休息时长
        statisticDataOut.setFatigueDrivingDuration(ArithmeticUtils.timeMsToSecond(var.getDrivingTotalTimeMs()-var.getRestTotalTimeMs()));
    }


    /**
     * 检查当前是否为疲劳状态，并结束疲劳状态
     */
    private void endFatigueDriving() {
        Optional.ofNullable(drivingData.getEventData(EVENT_FATIGUE_DRIVING)).ifPresent(eventData ->{
            eventData.setExtendField01(getObj().getDrivingTotalTimeMs());
            EventDataConvert.convertEnd(eventData, drivingData);
            drivingData.addEventDataToList(eventData);
            drivingData.removeEventData(EVENT_FATIGUE_DRIVING);
        });
    }
}
